<?php
/*****************************************
 This file is part of the Dynamo Core CMS
 Copyright (c) Dynamo Studios 2008

 *** NOTE ***
 Permission is hereby granted to execute this file as a
 web service under the terms of the license agreement.

 See LICENSE.txt, in the system folder.

 All other rights, other than those expressed in the license
 document, are reserved.

 ****************************************/

/**
 * @name Data Storage Abstraction Layer (MySQL)
 * @version 0.3.2 (Alvin)
 * @abstract
 * Provides an interface to the mysql database.
 *
 * @author Sean David Micklethwaite
 */

global	$db;

function dbInit()
{
	global $db;
	$db = mysql_connect(DBHOST, DBUSR, DBPWD)
		or die("Fatal error: mysql connection failed");
	mysql_select_db(DBNAME) or die(mysql_error($db));
}

function dbFormatSqlWc($s_val)
{
	return str_replace("\%", "*", str_replace("*", "%", $s_val));
}

function dbCreateQueryString($vzCriteria)
{
	$sQuery = "";
	$str = array();

	foreach($vzCriteria as $name => $val)
	{
		if($val !== "")
			array_push($str, "`$name` = '".mysql_real_escape_string($val)."'");
	};
	if(count($str))
		$sQuery = "WHERE ".implode(" AND ", $str);

	return $sQuery;
}

function dbReadByIndex($s_idx, $s_val, $vzz_objSet = null)
{
	$vzz_obj = array();

	//
	// Check index exists
	// TODO: Lookup in $index table, (validate with regex?)
	//




	//
	// Format wildcards to SQL
	//

	$s_fVal = dbFormatSqlWc($s_val = mysql_real_escape_string($s_val));


	//
	// Exec the query
	//

	if($s_val === $s_fVal)
	{
		$query = mysql_query("SELECT cnum AS `\$cnum`,id AS `\$id` FROM `i\$$s_idx` WHERE value = '" .
			$s_fVal . "'");
	}
	else
	{
		//
		// This is a wildcard search, so include the value in the result
		//

		$query = mysql_query("SELECT cnum AS `\$cnum`,id AS `\$id`,value AS `$s_idx` FROM `i\$$s_idx` WHERE value LIKE '" .
			$s_fVal . "' GROUP BY cnum,id");
	}


	if($query === false)
		throw new dyExDbError(mysql_error());

	if($vzz_objSet === null)
	{
		while($vz_obj = mysql_fetch_assoc($query))
		{
			array_push($vzz_obj, $vz_obj);
		}
	}
	else
	{
		while($vz_obj = mysql_fetch_assoc($query))
		{
			//
			// If we have a fixed set, check object is in the set
			// TODO: offload this work onto the SQL engine (faster)
			//

			if(($i_obj = dyFindRsItem($vzz_objSet, $vz_obj)) !== false)
			{
				//
				// Add object to array
				//

				array_push($vzz_obj, array_merge($vz_obj, $vzz_objSet[$i_obj]));
			};
		};
	}

	return $vzz_obj;
}

function dbReadObject($clsid, $id)
{
	$query = mysql_query("SELECT * FROM `$clsid` WHERE \$id = $id")
		or die(mysql_error());

	if(mysql_num_rows($query) != 1)
		return false;

	$obj = mysql_fetch_assoc($query);
	mysql_free_result($query);

	return $obj;
}

function dbDeleteObject($clsid, $id)
{
	$query = mysql_query("DELETE FROM `$clsid` WHERE \$id = $id")
		or die(mysql_error());

	return $query;
}

class cDbClass
{
	protected $miClassNum;
	protected $msClassId;
	protected $mzCoreAttributes;
    protected $msName;
    protected $msIcon;
    protected $mzToken;
    protected $mToken;

	function Read($byNum)
	{
		//
		// Execute the query
		//

		if($byNum && is_numeric($this->miClassNum))
		{
			$query = mysql_query("SELECT * FROM `\$class` WHERE cnum = "
				. $this->miClassNum);
		}
		else if (ctype_alnum($this->msClassId))
		{
			$query = mysql_query("SELECT * FROM `\$class` WHERE id = '"
				. $this->msClassId . "'");
		}
		else
			throw new dyExDbError("Cannot read class - invalid query");

		if($query == false)
			throw new dyExDbError(mysql_error());


		//
		// Check class was found
		//

		if(mysql_num_rows($query) != 1)
			throw new dyExNotFound("Class not found");

		//
		// Read results
		//

		$z_result = mysql_fetch_row($query);

		$this->miClassNum = $z_result[0];
		$this->msClassId = $z_result[1];
		$this->msName = $z_result[2];
        $this->msIcon = $z_result[3];
		$this->msDescription = $z_result[4];
        
        //
        // HACK!! Cast as float to work around PHP's
        // int size limit
        //
        $this->mzToken = array(
            (float)$z_result[5],
            (float)$z_result[6],
            (float)$z_result[7]);
		$this->mToken = (float)$z_result[8];

		mysql_free_result($query);


		//
		// Now read the class' core attributes
		//

		$query = mysql_query("SELECT id FROM `\$coreattrib` WHERE cnum = "
			. $this->miClassNum);

		if($query == false)
			throw new dyExDbError(mysql_error());

		while($z_row = mysql_fetch_row($query))
		{
			//
			// Set key to index ID, value to true
			//

			$this->mzCoreAttributes[$z_row[0]] = true;
		}

		return true;
	}
}

class cDbIndex
{
	protected $miIndexId;
	protected $msName;
	protected $miRefCount;
	protected $msRegEx;
	protected $mbModified;

	function Read($byId)
	{
		//
		// Execute the query
		//

		if($byId && is_numeric($this->miIndexId))
		{
			$query = mysql_query("SELECT * FROM `\$index` WHERE id = "
				. $this->miIndexId);
		}
		else if(ctype_alnum(str_replace('-', '', $this->msName)))
		{
			$query = mysql_query("SELECT * FROM `\$index` WHERE name = '"
				. $this->msName . "'");
		}
		else
			throw new dyExDbError("Cannot read index - invalid query ({$this->msName})");

		if($query == false)
			throw new dyExDbError(mysql_error());


		//
		// Check index was found
		//

		if(mysql_num_rows($query) != 1)
			throw new dyExNotFound("Cannot read index");

		//
		// Read results
		//

		$z_result = mysql_fetch_row($query);

		$this->miIndexId = $z_result[0];
		$this->msName = $z_result[1];
		$this->msRegEx = $z_result[2];

		$this->mbModified = false;


		mysql_free_result($query);

		return true;
	}

	function ReadValue($id_clsNum, $id_obj)
	{
		//
		// Validate arguments
		//

		if(!(is_numeric($id_clsNum) && is_numeric($id_obj)))
			throw new dyExDbError("Invalid query: cls#=$id_clsNum obj=$id_obj");

		//
		// Execute the query
		//

		$query = mysql_query("SELECT value FROM `i\${$this->msName}` WHERE cnum = "
			. $id_clsNum . " AND id = " . $id_obj);

		if($query == false) throw new dyExDbError(mysql_error());


		//
		// Return value(s)
		//

		$n = mysql_num_rows($query);
		
		if($n == 0)
			return null;
		else if ($n == 1)
			return mysql_result($query, 0);
		else{
			$vals = array();
			for($i=0; $i < $n; $i++)
				$vals[] = mysql_result($query, $i);
			return $vals;
		};
	}

	function WriteValue($id_clsNum, $id_obj, $s_val, $b_ins = false)
	{
		//
		// Validate object details
		//

		if(!(is_numeric($id_clsNum) && is_numeric($id_obj)))
			throw new dyExDbError("Cannot write index value - invalid query (clsNum=$id_clsNum, objId=$id_obj)");

		//
		// Validate value
		//

		if(ereg_replace($this->msRegEx, '', $s_val) !== '')
			throw new dyExValidation("Regular expression check failed on '{$this->msName}' for '$s_val', syntax is: {$this->msRegEx}");


		//
		// Fetch existing values for particular object, find out whether
		// the value already exists
		//

		$v_vals = $this->ReadValue($id_clsNum, $id_obj);

		if($v_vals === null)
			$b_ins = true;
		else if(is_array($v_vals))
		{
			$b_ins = (array_search($s_val, $v_vals) === false);
		}
		else if($v_vals != $s_val)
			$b_upd = true;

		if($b_ins)
		{
			//
			// Insert new row
			//
			
			$query = mysql_query("INSERT INTO `i\${$this->msName}` (id, cnum, value) VALUES ($id_obj, $id_clsNum, '". mysql_real_escape_string($s_val) ."')");
		}
		else if($b_upd)
		{
			//
			// Update existing row
			//

			$query = mysql_query("UPDATE `i\${$this->msName}` SET value = '".
				mysql_real_escape_string($s_val) ."' WHERE id=$id_obj AND cnum=$id_clsNum LIMIT 1");
		}

		if($query === false)
			throw new dyExDbError(mysql_error());
		else
			return true;
	}
}

class cDbObject extends cDycmsAbstractObject
{
	function Read()
	{
		//
		// Execute query
		//

		$query = mysql_query("SELECT * FROM `{$this->mClsId}` WHERE
\$id = {$this->mId} LIMIT 1")
			or dyDie(mysql_error());

		if(mysql_num_rows($query) != 1)
			throw new dyExNotFound();

		$this->mAttributes = mysql_fetch_assoc($query);
		$this->mbModified = false;

		mysql_free_result($query);
	}

	function DoWrite()
	{
		if($this->mId)
		{
			//
			// Generate SQL
			//

			$str = array();
			foreach($this->mAttributes as $name => $val)
			{
				array_push($str, "`$name` = '".mysql_real_escape_string($val)."'");
			};
			$str = implode(", ", $str);

			//
			// Execute the query
			//

			$res = mysql_query("UPDATE `{$this->mClsId}` SET $str WHERE ".'`$id` = '.$this->mId)
				or dyLog(mysql_error());
		}
		else
		{
			$namez = array();
			$valz = array();

			foreach($this->mAttributes as $name => $val)
			{
				if($name != '$id')
				{
					array_push($namez, "`$name`");
					array_push($valz, "'".mysql_real_escape_string($val)."'");
				};
			};

			$names = implode(", ", $namez);
			$vals = implode(", ", $valz);

			$res = mysql_query("INSERT INTO `{$this->mClsId}` ($names) VALUES ($vals)")
				or dyLog(mysql_error());

			if(!$res)
				throw new dyExDbError('Failed to create new object');

			$this->mId = mysql_insert_id();
		};

		$this->mbModified = false;
		return true;
	}
}

class cDbQuery
{
	protected $mCriteria;

	function __construct() {
		$this->mCriteria = array();
	}

	function DoObjectQuery($sClsId)
	{
		$sCriteria = dbCreateQueryString($this->mCriteria);

		dyLog("dbDoReadObjects('$sClsId', '$sCriteria')");
		$query = mysql_query("SELECT * FROM `$sClsId` " . $sCriteria)
			or dyLog(mysql_error());

		if(!$query)
			return NULL;

		$vzzObj = array();

		while($vzObj = mysql_fetch_assoc($query))
		{
			array_push($vzzObj, $vzObj);
		}

		mysql_free_result($query);

		return $vzzObj;
	}
}

class cDbAccessCtrl
{
	protected $miUserNum;
	protected $miClassNum;
	protected $miObjectId;
	protected $mType;
	protected $mbExists;
	
	protected function Read(){
		$query = mysql_query('SELECT type FROM `$acl` WHERE '
			.'cnum = '.intval($this->miClassNum)
			.' AND id = '.intval($this->miObjectId)
			.' AND unum = '.intval($this->miUserNum)) or
				dyLog(mysql_error());
		
		if(!$query)
			throw new dyExDbError('Failed to read ACE');
		
		if($this->mbExists = is_array($rs = mysql_fetch_row($query)))
			$this->mType = $rs[0];
		
		mysql_free_result($query);
		return $this->mbExists;
	}
	
	protected function Write(){
		if($this->mbExists){
			// Update row
			$query = mysql_query('UPDATE `$acl` SET ('
				.'type = '.intval($this->mType).') WHERE '
				.'cnum = '.intval($this->miClassNum)
				.',id = '.intval($this->miObjectId)
				.',unum = '.intval($this->miUserNum)) or
					dyLog(mysql_error());
			
			if(!$query)
				throw new dyExDbError('Failed to update ACE');
		}else{
			// Insert row
			$query = mysql_query('INSERT INTO `$acl` (cnum,id,unum,type) VALUES ('
				.intval($this->miClassNum).','
				.intval($this->miObjectId).','
				.intval($this->miUserNum).','
				.intval($this->mType).')') or
					dyLog(mysql_error());
			
			if(!$query)
				throw new dyExDbError('Failed to update ACE');
		}
	}
}

class cDbClient
{
	protected $mlUserNum;
	protected $mUserIdHash;
	protected $mPasswordHash;
	protected $mUserId;
	protected $mlParent;
	protected $mlPrivileges;
	protected $mszCapabilities;
	protected $msName;

	protected function Read()
	{
		//
		// Validate inputs
		//

		if(!(ctype_alnum($this->mUserIdHash) &&
			ctype_alnum($this->mPasswordHash)))
			throw new dyExInvalidOp("Validation Error");

		$query = mysql_query(
			"SELECT unum,uid,parent,privs,caps,name FROM `\$client` WHERE uidhash = '".
			$this->mUserIdHash."' AND pwdhash = '".
			$this->mPasswordHash."'")
		 	or dyDie(mysql_error());

	 	if($vz = mysql_fetch_array($query))
	 	{
	 		//
	 		// Read the client details
	 		//

	 		$this->mlUserNum = $vz[0];
	 		$this->mUserId = $vz[1];
	 		$this->mlParent= $vz[2];
	 		$this->mlPrivileges = $vz[3];
	 		$this->mszCapabilities = explode('$', $vz[4]);
			$this->msName = $vz[5];

	 		return true;
	 	}
	 	else return false;
	}

	protected function ForceRead($uNum) {
		//
		// Validate inputs
		//

		if(!is_numeric($uNum))
			throw new dyExInvalidOp("Validation Error");

		$query = mysql_query(
			"SELECT unum,uid,parent,privs,caps,name,pwdhash FROM `\$client` WHERE unum = $uNum");
		if(!$query) throw new dyExDbError(mysql_error());

		if(!($vz = mysql_fetch_array($query)))
			throw new dyExNotFound();

	 	//
		// Read the client details
		//

		$this->mlUserNum = $vz[0];
		$this->mUserId = $vz[1];
		$this->mlParent = $vz[2];
		$this->mlPrivileges = $vz[3];
		$this->mszCapabilities = explode(',', $vz[4]);
		$this->msName = $vz[5];
		$this->mPasswordHash = $vz[6];
	}

	protected function Write()
	{
		$s_caps = implode(',', $this->mszCapabilities);
		
		if($this->mlUserNum)
		{
			$query = mysql_query("UPDATE `\$client` SET uidhash = '".
				$this->mUserIdHash."',pwdhash = '".
				$this->mPasswordHash."',uid = '".
				mysql_real_escape_string($this->mUserId)."',parent = ".
				intval($this->mlParent).",privs = ".
				intval($this->mlPrivileges).",caps = '".
				mysql_real_escape_string($s_caps)."',name = '".
				mysql_real_escape_string($this->msName)."' WHERE unum = ".
				intval($this->mlUserNum)) or die(mysql_error());

			return true;
		}
		else
		{
			$query = mysql_query("INSERT INTO `\$client` (uidhash,pwdhash,uid,parent,privs,caps,name) VALUES ('".
				$this->mUserIdHash."','".
				$this->mPasswordHash."','".
				mysql_real_escape_string($this->mUserId)."',".
				intval($this->mlParent).",".
				intval($this->mlPrivileges).",'".
				mysql_real_escape_string($s_caps)."','".
				mysql_real_escape_string($this->msName)."')");

			if($query) {
				$this->mlUserNum = mysql_insert_id();
				return true;
			}else throw new dyExDbError(mysql_error());
		}
	}

	protected function DoDelete() {
		if($this->mlUserNum){
			$query = mysql_query('DELETE FROM `$client` WHERE unum='.$this->mlUserNum.' LIMIT 1');
			return ($query != null);
		} else return false;
	}
}

function dbGetOffset(){
    if(isset($_POST['$$db-offset']))
		return intval($_POST['$$db-offset']);
	else if(isset($_GET['$$db-offset']))
		return intval($_GET['$$db-offset']);
	else
		return 0;
}
function dbGetCount(){
    if(isset($_POST['$$db-count']))
		return intval($_POST['$$db-count']);
	else if(isset($_GET['$$db-count']))
		return intval($_GET['$$db-count']);
	else
		return 0x8FFFFFFF;
}


function dbReadAll($sTable){
	$offset = dbGetOffset();
	$count = dbGetCount();

	$query = mysql_query('SELECT * FROM '.$sTable.' LIMIT ' . $offset . ', ' . ($count + $offset));
	if(!$query) throw new dyExDbError(mysql_error());

	$all = array();
	while($row = mysql_fetch_assoc($query)) {
		$all[] = $row;
	};

	mysql_free_result($query);
	return $all;
}
function dbCountRows($sTable){
    $query = mysql_query('SELECT COUNT(*) FROM '.$sTable);
    $count = mysql_result($query,0);
    mysql_free_result($query);

    return $count;
}

function dbReadAllClients() {
	return dbReadAll('$client');
}
function dbReadAllClasses() {
	return dbReadAll('$class');
}
function dbReadAllObjects($clsid){
    return dbReadAll($clsid);
}
function dbCountClients() {
	return dbCountRows('$client');
}
function dbCountClasses() {
	return dbCountRows('$class');
}
function dbCountObjects($clsid){
    return dbCountRows($clsid);
}


?>
